// OpentTxl-C Version 11 options
// J.R. Cordy, Jan 2023

// Copyright 2023, James R. Cordy and others

// Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
// and associated documentation files (the “Software”), to deal in the Software without restriction, 
// including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all copies 
// or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 
// AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// TXL processor command line handling and option flags

// Modification Log

// v11.0 Initial revision, adapted from OpenTxl 11.0

// v11.3 Add -p[rogress] virtual option (information messages), default to -q[uiet]

// I/O, strings, memory allocation
#include "support.h"

// Global modules
#include "locale.h"
#include "limits.h"
#include "options.h"
#include "tokens.h"
#include "errors.h"
#include "charset.h"

// Check interface consistency
#include "options.h"

// Phase 
int phase;

// TXL program exit code
int exitcode;

// Option flags - 1-origin [1 .. lastOption]
bool options_option[lastOption + 1];

// Command line specified file names
string options_txlSourceFileName;
string options_inputSourceFileName;
string options_txlCompiledFileName;
string options_outputSourceFileName;

// Maximum output line length and indent
int options_outputLineLength;
int options_indentIncrement;

// TXL library
string options_txlLibrary;

// TXL include libraries - 1-origin [1 .. maxTxlIncludeLibs]
string options_txlIncludeLibs[maxTxlIncludeLibs + 1];
int options_nTxlIncludeLibs;    // 0

// Optional character maps
string options_optionIdChars;
string options_optionSpChars;

// Flag changes to character maps to scanner
bool options_updatedChars;

// TXL dynamic size limit (in Mb)
int options_txlSize;
int options_transformSize;

// TXL program arguments - 1-origin [1 .. maxProgramArguments]
string options_progArgs[maxProgramArguments + 1];
int options_nProgArgs;  // 0

// TXL preprocessor symbols - 1-origin [1 .. maxIfdefSymbols]
string options_ifdefSymbols[maxIfdefSymbols + 1];
int options_nIfdefSymbols;  // 0

void options_setIfdefSymbol (const string symbol)
{
    if (options_nIfdefSymbols < maxIfdefSymbols) {
        options_nIfdefSymbols += 1;
        stringcpy (options_ifdefSymbols[options_nIfdefSymbols], symbol);
    } else {
        string message;
        stringprintf (message, "Too many preprocessor symbols (> %d)", maxIfdefSymbols);
        error ("", message, LIMIT_FATAL, 154);
    }
}

int options_lookupIfdefSymbol (const string symbol)
{
    for (int i = 1; i <= options_nIfdefSymbols; i++) {
        if (stringcmp (options_ifdefSymbols[i], symbol) == 0) {
            return (i);
        }
    }
    return (0);
}

void options_unsetIfdefSymbol (const string symbol)
{
    int symbolIndex = options_lookupIfdefSymbol (symbol);
    if (symbolIndex != NOT_FOUND) {
        stringcpy (options_ifdefSymbols[symbolIndex], "");
    } else {
        // No need for error message in this case; just do nothing
    }
}

// Version and usage messages
#ifndef STANDALONE 
    #define usage  "Usage:  txl [txloptions] [-o outputfile] inputfile [txlfile] [- progoptions]"
#else
    #define usage  "Command arguments: [-s N] [-w N] [-o outputfile] [progoptions] inputfile"
#endif

static void options_useerror (const bool pragma)
{
    if (pragma) {
        error ("", "Unrecognized command line option in #pragma", DEFERRED, 946);
    }
#ifndef STANDALONE
    fprintf (stderr, "%s\n", version);
    fprintf (stderr, "%s\n", usage);
    fprintf (stderr, "%s\n", "(for more information use txl -help)");
#else
    fprintf (stderr, "%s\n", usage);
#endif
    throw (QUIT);
}

static void options_help (void) {
#ifndef STANDALONE
    fprintf (stderr, "%s\n", version);
    fprintf (stderr, "%s\n", usage);
    fprintf (stderr, "%s\n", "");
    fprintf (stderr, "%s\n", "'txl' invokes the TXL processor to transform an input file using a TXL program.");
    fprintf (stderr, "%s\n", "The input file to be transformed is 'inputfile' and the TXL program to");
    fprintf (stderr, "%s\n", "transform it is 'txlfile'.  The 'txlfile' must be named ending in '.txl',");
    fprintf (stderr, "%s\n", "and is normally either in the present working directory, the 'txl' subdirectory");
    fprintf (stderr, "%s\n", "of the present working directory, or the TXL library ('");
    fprintf (stderr, "%s\n", options_txlLibrary);
    fprintf (stderr, "%s\n", "').");
    fprintf (stderr, "%s\n", "If no 'txlfile' is given, it is inferred from the suffix of the input file.");
    fprintf (stderr, "%s\n", "e.g., inputfile 'test.c' infers txlfile 'c.txl'.");
    fprintf (stderr, "%s\n", "");
    fprintf (stderr, "%s\n", "Command options:");
    fprintf (stderr, "%s\n", "  -q               Quiet - turn off all information messages (default)");
    fprintf (stderr, "%s\n", "  -p               Progress - turn on information messages");
    fprintf (stderr, "%s\n", "  -v               Verbose - more detail in information messages");
    #ifdef NOLOADSTORE
    fprintf (stderr, "%s\n", "  -c               Compile program to TXL byte code file 'txlfile.ctxl'");
    fprintf (stderr, "%s\n", "  -l               Load and run TXL byte code file 'txlfile.ctxl'");
    #endif
    fprintf (stderr, "%s\n", "  -d <symbol>      Define preprocessor symbol <symbol>");
    fprintf (stderr, "%s\n", "  -i <dir>         Add <dir> to the TXL include file search path");
    fprintf (stderr, "%s\n", "  -comment         Parse comments in input (default ignored)");
    fprintf (stderr, "%s\n", "  -char            Parse white space and newlines in input (default ignored)");
    fprintf (stderr, "%s\n", "  -newline         Parse newlines in input (default ignored)");
    fprintf (stderr, "%s\n", "  -multiline       Allow multiline tokens (default yes)");
    fprintf (stderr, "%s\n", "  -token           Ignore white space in input (separates input only - default)");
    fprintf (stderr, "%s\n", "  -id '<chars>'    Add each of <chars> to the identifier character set");
    fprintf (stderr, "%s\n", "  -sp '<chars>'    Add each of <chars> to the white space character set");
    fprintf (stderr, "%s\n", "  -esc '<char>'    Use <char> as the literal string escape character");
    fprintf (stderr, "%s\n", "  -upper           Shift input to upper case (except inside literals)");
    fprintf (stderr, "%s\n", "  -lower           Shift input to lower case (except inside literals)");
    fprintf (stderr, "%s\n", "  -case            Ignore case in input (but do not change it)");
    fprintf (stderr, "%s\n", "  -txl             Use TXL input lexical conventions");
    fprintf (stderr, "%s\n", "  -attr            Show attributes in output source (default invisible)");
    fprintf (stderr, "%s\n", "  -raw             Output source in raw (unspaced) form");
    fprintf (stderr, "%s\n", "  -w <width>       Wrap output source lines at <width> characters (default 128)");
    fprintf (stderr, "%s\n", "  -in <width>      Use output indent width of <width> characters (default 4)");
    fprintf (stderr, "%s\n", "  -tabnl           [TAB_nn] may force line wrap (default no)");
    fprintf (stderr, "%s\n", "  -xml             Output as XML parse tree");
    fprintf (stderr, "%s\n", "  -s <size>        Expand TXL tree space memory to <size> Mb (default 32)");
    fprintf (stderr, "%s\n", "  -analyze         Analyze grammar and rule set for ambiguities (slow)");
    fprintf (stderr, "%s\n", "  -u               Show TXL tree space memory usage statistics");
    fprintf (stderr, "%s\n", "  -o <file>        Write output to <file> (default standard output)");
    fprintf (stderr, "%s\n", "  -no<option>      Turn off <option> (e.g., -noraw)");
    fprintf (stderr, "%s\n", "  - <progoptions>  Pass <progoptions> to TXL program in global variable 'argv'");
    fprintf (stderr, "%s\n", "");
    fprintf (stderr, "%s\n", "Debugging options, output to standard error stream:");
    fprintf (stderr, "%s\n", "  -Dscan           Show scanner input tokens");
    fprintf (stderr, "%s\n", "  -Dparse          Show parser input parse tree");
    fprintf (stderr, "%s\n", "  -Dresult         Show transformer final result parse tree");
    fprintf (stderr, "%s\n", "  -Dgrammar        Show grammar as a parse tree schema");
    fprintf (stderr, "%s\n", "  -Dpattern        Show pattern and replacement parse tree schemas");
    fprintf (stderr, "%s\n", "  -Drules          Show names of rules as they are applied");
    fprintf (stderr, "%s\n", "  -Dapply          Show trace of transformations by rules");
    #ifdef DEBUG 
        fprintf (stderr, "%s\n", "  -V               Show TXL version");
        fprintf (stderr, "%s\n", "  -Dtrees          Show partial parse trees as we parse (verbose)");
        fprintf (stderr, "%s\n", "  -Dstack          Show parse stack state at backtrack limit" );
        fprintf (stderr, "%s\n", "  -Dsharing        Show subtree sharing information (only if -Drules)");
        fprintf (stderr, "%s\n", "  -Dall            Turn on all debugging option (deadly verbose)");
    #endif
#else
    fprintf (stderr, "%s\n", usage);
#endif
}

static void options_processOption (const string arg, const string arg2, int *argnum, const bool setting, const bool pragma)
{

    // Process an option flag
    int arglength = stringlen (arg);

    if ((stringchar (arg, 1) != '-') || (arglength < 2)) {
        return;
    }

    // All options are unique in the first 2 characters
    char optionchar = stringchar (arg, 2);
    char optionchar2 = ' ';

    if (arglength > 2) {
        optionchar2 = stringchar (arg, 3);
    }

    if (((optionchar == 'h') || (stringcmp (arg, "--help") == 0)) && (!pragma)) {
        options_help ();
        throw (QUIT);

    } else if ((optionchar == 'V') && (!pragma)) {
        fprintf (stderr, "%s\n", version);
        throw (QUIT);

    } else if (optionchar == 'v') {
        options_option[verbose_p] = setting;
        options_option[quiet_p] = !setting;

    } else if (optionchar == 'p') {
        options_option[quiet_p] = !setting;
        options_option[verbose_p] = !setting;

    } else if (optionchar == 'q') {
        options_option[quiet_p] = setting;
        options_option[verbose_p] = !setting;

    } else if (optionchar == 'o') {
        (*argnum)++;
        if (stringcmp (arg2, "") != 0) {
            stringcpy (options_outputSourceFileName, arg2);
        }

    } else if (optionchar == 'w') {
        (*argnum)++;
        if (stringcmp (arg2, "") != 0) {
            string newWidthString;
            stringcpy (newWidthString, arg2);
            int newWidth = 0;
            while (true) {
                if ((stringlen (newWidthString) == 0) || (stringchar (newWidthString, 1) < '0') || (stringchar (newWidthString, 1) > '9')) break;
                newWidth = (newWidth * 10) + (stringchar (newWidthString, 1) - '0');
                substring (newWidthString, newWidthString, 2, stringlen (newWidthString));
            }
            options_outputLineLength =  max (20, newWidth);
            options_option[width_p] = setting;
        }

    } else if ((optionchar == 's') && ((optionchar2 == 'i') || (optionchar2 == ' '))) {
        (*argnum)++;
        if (stringcmp (arg2, "") != 0) {
            string newSizeString;
            stringcpy (newSizeString, arg2);
            int newSize = 0;
            while (true) {
                if ((stringlen (newSizeString) == 0) || (stringchar (newSizeString, 1) < '0') || (stringchar (newSizeString, 1) > '9')) break;
                newSize = (newSize * 10) + (stringchar (newSizeString, 1) - '0');
                substring (newSizeString, newSizeString, 2, stringlen (newSizeString));
            }
            if (newSize < 1) {
                newSize = 1;
            } else if (newSize > 10000) {
                newSize = 10000;
            }
            options_txlSize = newSize;
            options_transformSize = newSize;
            options_option[size_p] = setting;
        }

    } else if (optionchar == 'x') {
        options_option[xmlout_p] = setting;

    } else if ((optionchar == 't') && (optionchar2 == 'o')) {
        options_option[charinput_p] = !setting;
        options_option[raw_p] = options_option[charinput_p];

    } else if ((optionchar == 'c') && (optionchar2 == 'h')) {
        options_option[charinput_p] = setting;
        options_option[raw_p] = options_option[charinput_p];

    } else if ((optionchar == 'r') && (optionchar2 == 'a')) {
        options_option[raw_p] = setting;

    } else if ((optionchar == 'n') && (optionchar2 == 'e')) {
        options_option[newline_p] = setting;

    } else if (((optionchar == 'L') || ((optionchar == 'd') && (optionchar2 == 'i'))) && (!pragma)) {
        (*argnum)++;
        if (stringcmp (arg2, "") != 0) {
            stringcpy (options_txlLibrary, arg2);
            stringcpy (options_txlIncludeLibs[options_nTxlIncludeLibs], options_txlLibrary);
        }

    } else if ((((optionchar == 'i') || (optionchar == 'I')) && (optionchar2 == ' ')) && (!pragma)) {
        (*argnum)++;
        if (stringcmp (arg2, "") != 0) {
            if (options_nTxlIncludeLibs == 10) {
                string message;
                stringprintf (message, "Too many -i include directories (> %d)", maxTxlIncludeLibs);
                error ("", message, LIMIT_FATAL, 941);
            }
            options_nTxlIncludeLibs += 1;
            stringcpy (options_txlIncludeLibs[options_nTxlIncludeLibs], options_txlIncludeLibs[options_nTxlIncludeLibs - 1]);
            stringcpy (options_txlIncludeLibs[options_nTxlIncludeLibs - 1], arg2);
        }

    } else if ((optionchar == 'a') && (optionchar2 == 'n')) {
        options_option[analyze_p] = setting;

    } else if ((optionchar == 'a') && (optionchar2 == 't')) {
        options_option[attr_p] = setting;

    } else if ((optionchar == 'r') && (optionchar2 == 'a')) {
        options_option[raw_p] = setting;

    } else if ((optionchar == 't') && (optionchar2 == 'o')) {
        options_option[charinput_p] = !setting;
        options_option[raw_p] = options_option[charinput_p];

    } else if ((optionchar == 't') && (stringcmp (arg, "-tabnl") == 0)) {
        options_option[notabnl_p] = !setting;

    } else if ((optionchar == 't') && (optionchar2 == 'x')) {
        options_option[txl_p] = setting;

    } else if ((optionchar == 'i') && (optionchar2 == 'n')) {
        (*argnum)++;
        if (stringcmp (arg2, "") != 0) {
            string newIndentString;
            stringcpy (newIndentString, arg2);
            int newIndent = 0;
            while (true) {
                if ((stringlen (newIndentString) == 0) || (stringchar (newIndentString, 1) < '0') || (stringchar (newIndentString, 1) > '9')) break;
                newIndent = (newIndent * 10) + (stringchar (newIndentString, 1) - '0');
                substring (newIndentString, newIndentString, 2, stringlen (newIndentString));
            }
            options_indentIncrement =  min (10, newIndent);
            options_option[indent_p] = setting;
        }

    } else if ((optionchar == 'u') && (optionchar2 == 'p')) {
        options_option[upper_p] = setting;

    } else if ((optionchar == 'u') && ((optionchar2 == 's') || (optionchar2 == ' '))) {
        options_option[usage_p] = setting;

    } else if ((optionchar == 'c') && (optionchar2 == 'o')) {
        options_option[comment_token_p] = setting;

    } else if ((optionchar == 'c') && (optionchar2 == 'h')) {
        options_option[charinput_p] = setting;
        options_option[raw_p] = options_option[charinput_p];

    } else if ((optionchar == 'c') && (optionchar2 == 'a')) {
        options_option[case_p] = setting;

#ifndef NOLOADSTORE
    } else if (((optionchar == 'c') && (optionchar2 == ' ')) && (!pragma)) {
        options_option[compile_p] = setting;
#endif

    } else if ((optionchar == 'n') && (optionchar2 == 'e')) {
        options_option[newline_p] = setting;

    } else if (optionchar == 'm') {
        options_option[multiline_p] = setting;

    } else if ((optionchar == 'i') && (optionchar2 == 'd')) {
        (*argnum)++;
        if (stringcmp (arg2, "") != 0) {
            stringcpy (options_optionIdChars, arg2);
            for (int idc = 1; idc <= stringlen (options_optionIdChars); idc++) {
                unsigned char idchar = options_optionIdChars[idc - 1];
                charset_addIdChar (idchar, setting);
            }
        }
        options_updatedChars = 1;

    } else if ((optionchar == 's') && (optionchar2 == 'p')) {
        (*argnum)++;
        if (stringcmp (arg2, "") != 0) {
            stringcpy (options_optionSpChars, arg2);
            for (int spc = 1; spc <= stringlen (options_optionSpChars); spc++) {
                unsigned char spchar = options_optionSpChars[spc - 1];
                charset_addSpaceChar (spchar, setting);
            }
            options_updatedChars = 1;
        }

    } else if (optionchar == 'e') {
        (*argnum)++;
        if (stringcmp (arg2, "") != 0) {
            charset_setEscapeChar (stringchar (arg2, 1), setting);
        }

    } else if (optionchar == 'd') {
        (*argnum)++;
        if (stringcmp (arg2, "") != 0) {
            options_setIfdefSymbol (arg2);
        }

    } else if ((optionchar == 'l') && (optionchar2 == 'o')) {
        options_option[lower_p] = setting;

#ifndef NOLOADSTORE
    } else if (((optionchar == 'l') && (optionchar2 == ' ')) && (!pragma)) {
        options_option[load_p] = setting;
#endif

    } else if (optionchar == 'D') {
        string dbarg;
        substring (dbarg, arg, 3, stringlen (arg));     // -DXXXX

        if (stringcmp (dbarg, "parse") == 0) {
            options_option[parse_print_p] = setting;

        } else if ((stringcmp (dbarg, "pattern") == 0) || (stringcmp (dbarg, "patterns") == 0)) {
            options_option[pattern_print_p] = setting;

        } else if (stringcmp (dbarg, "apply") == 0) {
            options_option[apply_print_p] = setting;

        } else if (stringcmp (dbarg, "rules") == 0) {
            options_option[rule_print_p] = setting;

        } else if ((stringcmp (dbarg, "result") == 0) || (stringcmp (dbarg, "final") == 0)) {
            options_option[result_tree_print_p] = setting;

    #ifdef DEBUG
        } else if (stringcmp (dbarg, "boot") == 0) {
            options_option[boot_parse_p] = setting;
    #endif
        } else if (stringcmp (dbarg, "grammar") == 0) {
            options_option[grammar_print_p] = setting;

        } else if (stringcmp (dbarg, "sharing") == 0) {
            options_option[sharing_p] = setting;

        } else if (stringcmp (dbarg, "trees") == 0) {
            options_option[tree_print_p] = setting;

        } else if (stringcmp (dbarg, "stack") == 0) {
            options_option[stack_print_p] = setting;

        } else if ((stringcmp (dbarg, "scan") == 0) || (stringcmp (dbarg, "tokens") == 0)) {
            options_option[tokens_p] = setting;

        } else if (stringcmp (dbarg, "all") == 0) {
            options_option[parse_print_p] = setting;
            options_option[apply_print_p] = setting;
            options_option[tree_print_p] = setting;
            options_option[rule_print_p] = setting;
            options_option[result_tree_print_p] = setting;
            options_option[tokens_p] = setting;

        } else {
            string message;
            stringprintf (message, "Unrecognized debugging option '%s'", arg);
        }

    } else if (optionchar == 'Z') {
        options_option[darren_p] = setting;

    } else if ((optionchar == 'n') && (optionchar2 == 'o')) {
        string subarg, noarg;
        substring (subarg, arg, 4, stringlen (arg)); // -noXXXX
        stringcpy (noarg, "-"), stringcat (noarg, subarg);
        options_processOption (noarg, arg2, argnum, false, pragma);

    } else if ((optionchar == '-') && (stringlen (arg) > 2)) {
        string subarg, noarg;
        substring (subarg, arg, 3, stringlen (arg)); // --XXXX
        stringcpy (noarg, "-"), stringcat (noarg, subarg);
        options_processOption (noarg, arg2, argnum, false, pragma);

    } else {
        #ifdef STANDALONE
            // Must be a user argument
            if (options_nProgArgs < maxProgramArguments) {
                options_nProgArgs++;
                stringprintf (options_progArgs[options_nProgArgs], "\"%s\"", arg);
            }
        #else
            // Bad option flag
            options_useerror (pragma);
        #endif
    }
}

void options_processOptionsString (const string optionsString)
{
    // Handle #pragma or [pragma] options string
    options_updatedChars = false;  // flag character map changes to scanner
    int nextchar = 1;
    int optionsStringLength = stringlen (optionsString);
    while (true) {
        if (nextchar > optionsStringLength) break;


        if (optionsString[nextchar - 1] == '-') {
            string option;
            stringcpy (option, &(optionsString[nextchar - 1]));
            string option2;
            stringcpy (option2, "");
            int lengthoption2 = 0;
            int spaceindex = stringindex (option, " ");

            if (spaceindex != 0) {
                substring (option, option, 1, (spaceindex - 1));
                substring (option2, optionsString, (nextchar + spaceindex), stringlen(optionsString));

                if (stringindex (option2, " ") != 0) {
                    substring (option2, option2, 1, (stringindex (option2, " ") - 1));
                } else if (stringindex (option2, "\n") == stringlen (option2)) {
                    substring (option2, option2, 1, (stringlen (option2) - 1));
                }
                lengthoption2 = stringlen (option2);

                if ((stringchar (option2, 1) == '\'') || (stringchar (option2, 1) == '"')) {
                    substring (option2, option2, 2, (stringlen (option2) - 1));
                }
            } else if (stringindex (option, "\n") == stringlen (option)) {
                substring (option, option, 1, (stringlen (option) - 1));
            }

            nextchar += stringlen (option);

            int optionsaccepted = 1;

            options_processOption (option, option2, &(optionsaccepted), true, true);

            if ((stringcmp (option2, "") != 0) && (optionsaccepted > 1)) {
                nextchar += 1 + lengthoption2;
            }

        } else {
            nextchar += 1;
        }
    }
}

#ifdef STANDALONE
    void options_ctxlgettxlsize (int *size)
    {
        extern addressint TXL_CTXL;
        typedef unsigned char bytearray[sizeof(int)];
        int ctxlptr = sizeof(int);      // skip MAGNUM
        for (int b = 0; b <= sizeof(int) - 1; b++) {
            (* (bytearray *) size)[b] = (* (bytearray *) TXL_CTXL)[ctxlptr];
            ctxlptr += 1;
        }
    }
#endif

// Initialization
void options (void) {
    // TXL exit code
    exitcode = 0;

    for (int opt = firstOption; opt <= lastOption; opt++) {
        options_option[opt] = false;
    }

    // Default multiline and no newlines on [TAB_NN]
    options_option[multiline_p] = true;
    options_option[notabnl_p] = true;

    // Command line specified file names
    stringcpy (options_txlSourceFileName, "");
    stringcpy (options_inputSourceFileName, "");
    stringcpy (options_txlCompiledFileName, "");
    stringcpy (options_outputSourceFileName, "");

    // Default maximum output line length and indent
    options_outputLineLength = defaultOutputLineLength;
    options_indentIncrement = 4;

    // Default TXL library
    stringcpy (options_txlLibrary, "/usr/local/lib/txl");

    // Default TXL include libraries - 1-origin [1 .. maxTxlIncludeLibs]
    stringcpy (options_txlIncludeLibs[0], "UNUSED");
    stringcpy (options_txlIncludeLibs[1], "txl");
    stringcpy (options_txlIncludeLibs[2], options_txlLibrary);
    options_nTxlIncludeLibs = 2;

    // Optional character maps
    stringcpy (options_optionIdChars, "");
    stringcpy (options_optionSpChars, "");

    // Flag changes to character maps to scanner
    options_updatedChars = false;

    // Default TXL dynamic size limit (in Mb)
    options_txlSize = defaultTxlSize;
    options_transformSize = defaultTxlSize;

    // Default no TXL program arguments
    options_nProgArgs = 0;

    // Default no TXL preprocessor symbols
    options_nIfdefSymbols = 0;

    // Get command line arguments
    int options_argcount = tfargcount;

    // Perhaps they just want to know how to use TXL
    if (options_argcount == 0) {
        options_useerror (false);
    }

    // By default we are quiet
    options_option[quiet_p] = true;

    // Handle command line args, see if they make sense
    int options_argnum = 1;
    options_nProgArgs = 0;

    while (true) {
        if (options_argnum > options_argcount) break;

        string arg;
        tffetcharg (options_argnum, arg);

        if ((stringlen (arg) > 1) && (arg[0] == '-')) {
            // A TXL command line option
            if (options_argnum < options_argcount) {
                string nextarg;
                tffetcharg (options_argnum + 1, nextarg);
                options_processOption (arg, nextarg, &(options_argnum), true, false);
            } else {
                options_processOption (arg, "", &(options_argnum), true, false);
            }

        } else if (stringcmp (arg, "-") == 0) {
            // TXL program's own arguments follow
            for (int progargnum = options_argnum + 1; progargnum <= options_argcount; progargnum++) {
                if (options_nProgArgs == maxProgramArguments) break;
                string nextarg, quotedNextarg;
                tffetcharg (progargnum, nextarg);
                stringprintf (quotedNextarg, "\"%s\"", nextarg);
                options_nProgArgs += 1;
                stringcpy (options_progArgs[options_nProgArgs], quotedNextarg);
            }
            break;

        } else {
            // Input or TXL program file name
            if ((stringcmp (options_inputSourceFileName, "") == 0) && (stringcmp (arg, "") != 0)) {
                stringcpy (options_inputSourceFileName, arg);

        #ifdef STANDALONE
            } else if (options_nProgArgs < maxProgramArguments) {
                // Must be a user argument
                string quotedNextarg;
                stringprintf (quotedNextarg, "\"%s\"", arg);
                options_nProgArgs++; 
                stringcpy (options_progArgs[options_nProgArgs], quotedNextarg);
        #else
            } else if ((stringcmp (options_txlSourceFileName, "") == 0) && (stringcmp (arg, "") != 0)) {
                // Should be a TXL program file name
                stringcpy (options_txlSourceFileName, arg);
            } else {
                // Screwed up arg files
                options_useerror (false);
        #endif
            }
        }
        options_argnum += 1;
    }

    // If we have no input file and no TXL file, there's nothing to do!
    if ((stringcmp (options_inputSourceFileName, "") == 0) && (stringcmp (options_txlSourceFileName, "") == 0)) {
        options_useerror (false);
    }

#ifndef STANDALONE
    // Object source file and TXL source file reversed when used with #! - JRC 11.12.07
    if (((stringcmp (options_txlSourceFileName, "") != 0) && (stringindex (options_txlSourceFileName, ".txl") == 0)) 
            && ((stringindex (options_inputSourceFileName, ".txl") != 0) || (stringindex (options_inputSourceFileName, ".ctxl") != 0))) {
        string oldTxlSourceFileName;
        stringcpy (oldTxlSourceFileName, options_txlSourceFileName);
        stringcpy (options_txlSourceFileName, options_inputSourceFileName);
        stringcpy (options_inputSourceFileName, oldTxlSourceFileName);
    }

    // Infer TXL program file name if necessary
    if (stringcmp (options_txlSourceFileName, "") == 0) {
        if ((options_option[compile_p]) && (stringindex (options_inputSourceFileName, ".txl") != 0)) {
            stringcpy (options_txlSourceFileName, options_inputSourceFileName);
            stringcpy (options_inputSourceFileName, "");
        } else {
            // Must infer TXL file name from input file extension
            int s = stringlen (options_inputSourceFileName);
            while (true) {
                if ((s == 1) || (options_inputSourceFileName [s - 1 - 1] == '.')) break;
                s -= 1;
            }
            stringprintf (options_txlSourceFileName, "%s.txl", &(options_inputSourceFileName[s - 1]));
        }
    }

    // Make sure we can open the TXL program file
    int options_sf = 0;
    tfopen (OPEN_CHAR_READ, options_txlSourceFileName, &options_sf);

    if (options_sf == 0) {
        // Perhaps it's lower case?
        string oldTxlSourceFileName;
        stringcpy (oldTxlSourceFileName, options_txlSourceFileName);
        {
            substring (options_txlSourceFileName, options_txlSourceFileName, 1, (stringlen (options_txlSourceFileName) - 4));
            stringcat (options_txlSourceFileName, ".txl");
        }
        tfopen (OPEN_CHAR_READ, options_txlSourceFileName, &options_sf);
        if (options_sf == 0) {
            stringcpy (options_txlSourceFileName, oldTxlSourceFileName);
        }
    }

    if (options_sf == 0) {
        // Perhaps it's just a grammar
        string oldTxlSourceFileName;
        stringcpy (oldTxlSourceFileName, options_txlSourceFileName);
        {
            substring (options_txlSourceFileName, options_txlSourceFileName, 1, (stringlen (options_txlSourceFileName) - 4));
            stringcat (options_txlSourceFileName, ".grm");
        }
        tfopen (OPEN_CHAR_READ, options_txlSourceFileName, &options_sf);
        if (options_sf == 0) {
            stringcpy (options_txlSourceFileName, oldTxlSourceFileName);
        }
    }

    if (options_sf == 0) {
        // Perhaps it's in one of the library subdirectories
        string oldTxlSourceFileName;
        stringcpy (oldTxlSourceFileName, options_txlSourceFileName);
        tfopen (OPEN_CHAR_READ, options_txlSourceFileName, &options_sf);

        for (int i = 1; i <= options_nTxlIncludeLibs; i++) {
            string __x3826;
            stringcpy (__x3826, options_txlIncludeLibs[i - 1]), stringcat (__x3826, "/");
            string __x3825;
            stringcpy (__x3825, __x3826), stringcat (__x3825, oldTxlSourceFileName);
            stringcpy (options_txlSourceFileName, __x3825);
            tfopen (OPEN_CHAR_READ, options_txlSourceFileName, &options_sf);
            if (options_sf != 0) break;

            // Perhaps it is lower case
            substring (options_txlSourceFileName, options_txlSourceFileName, 1, (stringlen (options_txlSourceFileName) - 4));
            stringcat (options_txlSourceFileName, ".txl");
            tfopen (OPEN_CHAR_READ, options_txlSourceFileName, &options_sf);

            if (options_sf != 0) break;

            // Perhaps it's just a grammar
            substring (options_txlSourceFileName, options_txlSourceFileName, 1, (stringlen (options_txlSourceFileName) - 4));
            stringcat (options_txlSourceFileName, ".grm");
            tfopen (OPEN_CHAR_READ, options_txlSourceFileName, &options_sf);

            if (options_sf != 0) break;
        }

        if (options_sf == 0) {
            stringcpy (options_txlSourceFileName, oldTxlSourceFileName);
        }
    }

    // Make sure that we actually found it
    if (options_sf == 0) {
        string message;
        stringprintf (message, "Can't find TXL program file '%s'", options_txlSourceFileName);
        error ("", message, FATAL, 944);
    }
    
    assert (options_sf != 0);
    tfclose (options_sf);

    #ifndef NOLOADSTORE
        if ((options_option[compile_p]) || (options_option[load_p])) {
            // Infer compiled program file name if necessary
            int sflength = stringlen (options_txlSourceFileName);
            if (sflength > 4) {
                string dotTxl;
                substring (dotTxl, options_txlSourceFileName, sflength - 3, sflength);
                if ((stringcmp (dotTxl, ".txl") == 0) || (stringcmp (dotTxl, ".grm") == 0)) {
                        substring (options_txlCompiledFileName, options_txlSourceFileName, 1, sflength - 4), 
                        stringcat (options_txlCompiledFileName, ".ctxl");
                    } else {
                        stringcpy (options_txlCompiledFileName, options_txlSourceFileName);
                }
            } else {
                stringcpy (options_txlCompiledFileName, options_txlSourceFileName);
            }
        }
      
        // If loading, attempt to open the compiled program file and get size
        if (options_option[load_p]) {
            int tf;
            tfopen (OPEN_BINARY_READ, options_txlCompiledFileName, &tf);
            if (tf == 0) {
                string message;
                stringprintf (message, "Can't open TXL load file '%s'", options_txlCompiledFileName);
                error ("", message, FATAL, 945);
            }
            int mn;
            tfread (&mn, sizeof (mn), tf);
            tfread (&options_txlSize, sizeof (options_txlSize), tf);
            if (!(options_option[size_p])) {
                options_transformSize = options_txlSize;
            }
            tfclose (tf);
        }
    #endif

#else
   // Standalone program - get the compiled size from the compiled byte array 
   options_option[load_p] = true;
   options_ctxlgettxlsize (&(options_txlSize));
   if (!(options_option[size_p])) {
       options_transformSize = options_txlSize;
   }
#endif
}
